package schedualing;

import java.util.ArrayList;

import adt.Process;
import adt.Queue;
import adt.User;

public class Fairshare {

	ArrayList<Process> allProcesses;
	ArrayList<User> allUsers;
	Queue schedule;
	public int clock;
	ArrayList<Process> addedProcesses;
	ArrayList<Process> finishedProcesses;

	public Fairshare(ArrayList<Process> allProcesses, ArrayList<User> allUsers) {
		this.allProcesses = allProcesses;
		this.allUsers = allUsers;
		schedule = new Queue(allProcesses.size());
		addedProcesses = new ArrayList<Process>();
		finishedProcesses = new ArrayList<Process>();
		clock = 0;
		for (User user : allUsers) {
			schedule.enqueue(user);
		}
		run();
	}

	public void run() {
		int totalTime = getTotalTime();
		User toRunUser;
		while (clock < totalTime) {
			addArrivedProcesses(clock);
			toRunUser = (User) schedule.dequeue();
			takeShare(toRunUser, totalTime);
			schedule.enqueue(toRunUser);

		}
	}

	public void takeShare(User user, int totalTime) {
		int startTime = clock;
		double userShare = getUserPriority(getTotaPriorties(), user)
				* totalTime;
		int finishTime = (int) (startTime + userShare);

		while (clock < finishTime) {
			Process toBeRuned = getProcessForUser(user);
			if (toBeRuned != null) {
				if (toBeRuned.remainingTime < 2) {
					System.out.print("At Time " + clock + " ");
					toBeRuned.run(1);
					clock += 1;
				} else {
					System.out.print("At Time " + clock + " ");
					toBeRuned.run(2);
					clock += 2;
				}
				addedProcesses.remove(toBeRuned);
				if (toBeRuned.remainingTime > 0) {
					addedProcesses.add(toBeRuned);
				} else {
					finishedProcesses.add(toBeRuned);
				}
			} else {
				System.out.println(user.userName + " Has No Processes.");
				return;
			}

		}

	}

	public void addArrivedProcesses(int clock) {

		for (int i = 0; i < allProcesses.size(); i++) {
			Process runningProcess = allProcesses.get(i);

			if (runningProcess.arrivalTime <= clock
					&& !addedProcesses.contains(runningProcess)
					&& !finishedProcesses.contains(runningProcess)) {
				addedProcesses.add(runningProcess);
			}
		}

	}

	public Process getProcessForUser(User user) {
		for (Process process : addedProcesses) {
			if (process.userName.equals(user.userName))
				return process;
		}
		return null;
	}

	public double getUserPriority(int totalPriorties, User user) {
		return ((double) user.priority / (double) totalPriorties);
	}

	public int getTotaPriorties() {
		int totalPriorties = 0;
		for (User user : allUsers) {
			totalPriorties += user.priority;
		}
		return totalPriorties;
	}

	public int getTotalTime() {
		int totalTime = 0;
		for (Process process : allProcesses)
			totalTime += process.runTime;
		return totalTime;
	}

}
